#include <jni.h>
#include <android/log.h>
#include <opencv2/opencv.hpp>

using namespace cv;

#define TAG "liuyi"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN , TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , TAG, __VA_ARGS__)

#define JNIREG_CLASS "com/sensetime/opencvdemo/ImageProcessUtils"//指定要注册的类

void printMat(Mat _data) {
    Mat data = cv::Mat_<double>(_data);
    for (int i = 0; i < data.rows; i++) {
        for (int j = 0; j < data.cols; j++) {
            LOGD("result = %f   \n", data.at<double>(i, j));
        }
    }
}

void DoPca(const Mat &_data, int dim, Mat &eigenvalues, Mat &eigenvectors) {
    assert(dim > 0);
    Mat data = cv::Mat_<double>(_data);

    int R = data.rows;
    int C = data.cols;

    if (dim > C)
        dim = C;

    //计算均值
    Mat m = Mat::zeros(1, C, data.type());

    for (int j = 0; j < C; j++) {
        for (int i = 0; i < R; i++) {
            m.at<double>(0, j) += data.at<double>(i, j);
        }
    }

    m = m / R;
    //求取6列数据对应的均值存放在m矩阵中，均值： [1.67、2.01、1.67、2.01、1.67、2.01]


    //计算协方差矩阵
    Mat S = Mat::zeros(R, C, data.type());
    for (int i = 0; i < R; i++) {
        for (int j = 0; j < C; j++) {
            S.at<double>(i, j) = data.at<double>(i, j) - m.at<double>(0, j); // 数据矩阵的值减去对应列的均值
        }
    }

    Mat Average = S.t() * S / (R);
    //计算协方差矩阵的方式----(S矩阵的转置 * S矩阵)/行数

    //使用opencv提供的eigen函数求特征值以及特征向量
    eigen(Average, eigenvalues, eigenvectors);
}

static void calculatePCA(JNIEnv *env, jclass type, jfloatArray deepthValues_) {

    jfloat *deepthValues = env->GetFloatArrayElements(deepthValues_, NULL);

    Mat DataMat = Mat::zeros(10, 6, CV_32F);

    //将数组A里的数据放入DataMat矩阵中
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 6; j++) {
            DataMat.at<float>(i, j) = deepthValues[i * 6 + j];
        }
    }

    // OPENCV PCA
    PCA pca(DataMat, noArray(), CV_PCA_DATA_AS_ROW);

    Mat eigenvalues;//特征值
    Mat eigenvectors;//特征向量

    DoPca(DataMat, 3, eigenvalues, eigenvectors);

    LOGD("eigenvalues:");
    printMat(eigenvalues);
    LOGD("\n");
    LOGD("eigenvectors:");
    printMat(eigenvectors);
}

static void lightenImage(JNIEnv *env, jclass type,
                         jintArray pixels_, jint w, jint h,
                         jint lightenValue) {
    jint *pixels = env->GetIntArrayElements(pixels_, NULL);

    if (pixels == NULL) {
        return;
    }
    Mat mat_image_src(h, w, CV_8UC4, (unsigned char *) pixels);
    Mat mat_image_clone = mat_image_src.clone();

    //saturate_cast 控制颜色的范围为0-255

    for (int i = 0; i < h; ++i) {
        for (int j = 0; j < w; ++j) {
            mat_image_clone.at<Vec3b>(i, j)[0] = saturate_cast<uchar>(
                    mat_image_src.at<Vec3b>(i, j)[0] + lightenValue);
            mat_image_clone.at<Vec3b>(i, j)[1] = saturate_cast<uchar>(
                    mat_image_src.at<Vec3b>(i, j)[1] + lightenValue);
            mat_image_clone.at<Vec3b>(i, j)[2] = saturate_cast<uchar>(
                    mat_image_src.at<Vec3b>(i, j)[2] + lightenValue);
        }
    }
//    openCV 的 转为 C 的
    cvtColor(mat_image_clone, mat_image_src, CV_RGB2RGBA, 4);


    env->ReleaseIntArrayElements(pixels_, pixels, 0);
}

static void blurImage(JNIEnv *env,
                      jclass jcls,
                      jintArray jarr_pixels,
                      jint j_width,
                      jint j_height) {
    // 获取java中传入的像素数组值，jintArray转化成jint指针数组
    jint *c_pixels = env->GetIntArrayElements(jarr_pixels, JNI_FALSE);
    if (c_pixels == NULL) {
        return;
    }

    LOGE("图片宽度：%d, 高度：%d", j_width, j_height);

    // 把c的图片数据转化成opencv的图片数据
    // 使用Mat创建图片
    Mat mat_image_src(j_height, j_width, CV_8UC4, (unsigned char *) c_pixels);
    // 选择和截取一段行范围的图片
    Mat temp = mat_image_src.rowRange(j_height / 3, 2 * j_height / 3);
    // 方框滤波
//    boxFilter(temp, temp, -1, Size(85, 85));
    // 均值滤波
    blur(temp, temp, Size(85, 85));
    // 使用高斯模糊滤波
//    GaussianBlur(temp, temp, Size(45, 13), 0, 0);
    // 将opencv图片转化成c图片数据，RGBA转化成灰度图4通道颜色数据
    cvtColor(temp, temp, CV_RGBA2GRAY, 4);

    // 更新java图片数组和释放c++中图片数组的值
    env->ReleaseIntArrayElements(jarr_pixels, c_pixels, JNI_FALSE);

}

static JNINativeMethod gMethods[] = {
        {"calculatePCA", "([F)V",      (void *) calculatePCA},
        {"lightenImage", "([IIII)V", (void *) lightenImage},
        {"blurImage",    "([III)V",  (void *) blurImage},
};

static int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *gMethods,
                                 int numMethods) {
    jclass clazz;
    clazz = env->FindClass(className);
    if (clazz == NULL) {
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

/*
* 为所有类注册本地方法
*/
static int registerNatives(JNIEnv *env) {
    int re = registerNativeMethods(env, JNIREG_CLASS, gMethods,
                                   sizeof(gMethods) / sizeof(gMethods[0]));
    return re;
}

/*
* System.loadLibrary("lib")时会调用
* 如果成功返回JNI版本, 失败返回-1
*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    jint result = -1;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    assert(env != NULL);
    if (!registerNatives(env)) {//注册
        return -1;
    }
    //成功
    result = JNI_VERSION_1_6;
    return result;
}






